// (C) 2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module espi_kcs_register(
    input                   clk,
    input                   reset_n,
    output reg  [ 31: 0]    readdata,
    output reg              readdatavalid,
    input       [  4: 0]    address,
    input                   chipselect,
    input                   write_n,
    input                   read_n,
    input       [ 31: 0]    writedata,
    output reg  [ 31: 0]    espi_kcs0_base,
    input                   espi_kcs0_ibf,
    input                   espi_kcs0_obf,
    input       [  1: 0]    espi_kcs0_status_bit01,
    output reg              espi_kcs0_sms_atn,
    input                   espi_kcs0_cd,
    input       [  7: 0]    espi_kcs0_din,
    output reg              espi_kcs0_din_rd,
    output reg  [  7: 0]    espi_kcs0_dout,
    output reg              espi_kcs0_dout_wr,
    input       [  7: 0]    espi_kcs0_datain_cnt,
    output reg  [  7: 0]    espi_kcs0_dataout_cnt,

    output reg  [ 31: 0]    espi_kcs1_base,
    input                   espi_kcs1_ibf,
    input                   espi_kcs1_obf,
    input       [  1: 0]    espi_kcs1_status_bit01,
    output reg              espi_kcs1_sms_atn,
    input                   espi_kcs1_cd,
    input       [  7: 0]    espi_kcs1_din,
    output reg              espi_kcs1_din_rd,
    output reg  [  7: 0]    espi_kcs1_dout,
    output reg              espi_kcs1_dout_wr,
    input       [  7: 0]    espi_kcs1_datain_cnt,
    output reg  [  7: 0]    espi_kcs1_dataout_cnt,
    // MSI-X
    input  wire         kcs_ier,
    output reg          espi_kcs0_msix_req,
    input  wire         espi_kcs0_msix_ack,
    output reg          espi_kcs1_msix_req,
    input  wire         espi_kcs1_msix_ack
);
wire        wr_strobe;
wire        rd_strobe;
wire [31:0] espi_kcs0_status;
wire [31:0] espi_kcs1_status;
reg         kcs0_din_rd;
reg         kcs0_din_rd_reg;
reg         kcs1_din_rd;
reg         kcs1_din_rd_reg;

reg         flg_kcs0_status_idle2wr;
reg         flg_kcs1_status_idle2wr;

reg  [1:0]  espi_kcs0_status_bit01_dly;
reg  [1:0]  espi_kcs1_status_bit01_dly;

reg       kcs0_msix_1d= 1'b0;
reg       kcs1_msix_1d= 1'b0;
wire      kcs0_msix;
wire      kcs1_msix;

assign espi_kcs0_status = {
            24'd0,
            espi_kcs0_status_bit01,2'b00,
            espi_kcs0_cd,1'b0, espi_kcs0_ibf,espi_kcs0_obf};

assign espi_kcs1_status = {
            24'd0,
            espi_kcs1_status_bit01,2'b00,
            espi_kcs1_cd,1'b0, espi_kcs1_ibf,espi_kcs1_obf};

assign wr_strobe = chipselect && ~write_n;
assign rd_strobe = chipselect && ~read_n;

always @(posedge clk )
begin
    if (reset_n == 0)
        espi_kcs0_base <= 8'd0;
    else if (wr_strobe &&(address==5'h00))
        espi_kcs0_base <= writedata;
end

always @(posedge clk )
begin
    if (reset_n == 0)
        espi_kcs1_base <= 8'd0;
    else if (wr_strobe &&(address==5'h08))
        espi_kcs1_base <= writedata;
end

always @(posedge clk )
begin
    if (reset_n == 0)
        espi_kcs0_sms_atn <= 1'd0;
    else if (wr_strobe &&(address==5'h01))
        espi_kcs0_sms_atn <= writedata[2];
end

always @(posedge clk )
begin
    if (reset_n == 0)
        espi_kcs1_sms_atn <= 1'd0;
    else if (wr_strobe &&(address==5'h09))
        espi_kcs1_sms_atn <= writedata[2];
end

always @(posedge clk )
begin
    if (rd_strobe &&(address==5'h02))
       kcs0_din_rd <= 1'b1;
    else
       kcs0_din_rd <= 1'b0;

    kcs0_din_rd_reg <= kcs0_din_rd;

    espi_kcs0_din_rd <=  kcs0_din_rd &&(~kcs0_din_rd_reg);
end

always @(posedge clk )
begin
    if (rd_strobe &&(address==5'h0a))
       kcs1_din_rd <= 1'b1;
    else
       kcs1_din_rd <= 1'b0;

    kcs1_din_rd_reg <= kcs1_din_rd;

    espi_kcs1_din_rd <=  kcs1_din_rd &&(~kcs1_din_rd_reg);
end

always @(posedge clk )
begin
    if (reset_n == 0)
        espi_kcs0_dout <= 8'd0;
    else if (wr_strobe &&(address==5'h03))
        espi_kcs0_dout <= writedata[7:0];
end

always @(posedge clk )
begin
    if (reset_n == 0)
        espi_kcs1_dout <= 8'd0;
    else if (wr_strobe &&(address==5'h0b))
        espi_kcs1_dout <= writedata[7:0];
end

always @(posedge clk )
begin
    if (wr_strobe &&(address==5'h03))
        espi_kcs0_dout_wr <= 1'd1;
    else
       espi_kcs0_dout_wr <= 1'd0;
end

always @(posedge clk )
begin
    if (wr_strobe &&(address==5'h0b))
        espi_kcs1_dout_wr <= 1'd1;
    else
       espi_kcs1_dout_wr <= 1'd0;
end

always @(posedge clk )
begin
    espi_kcs0_status_bit01_dly <= espi_kcs0_status_bit01;
    flg_kcs0_status_idle2wr <= (espi_kcs0_status_bit01_dly==2'b00) && (espi_kcs0_status_bit01==2'b10);
end

always @(posedge clk )
begin
    if (reset_n == 0)
        espi_kcs0_dataout_cnt <= 8'd0;
    else if(flg_kcs0_status_idle2wr) //idle2write status
        espi_kcs0_dataout_cnt <= 8'd0;
    else if (wr_strobe &&(address==5'h05))
        espi_kcs0_dataout_cnt <= writedata[7:0];
end

always @(posedge clk )
begin
    espi_kcs1_status_bit01_dly <= espi_kcs1_status_bit01;
    flg_kcs1_status_idle2wr <= (espi_kcs1_status_bit01_dly==2'b00) && (espi_kcs1_status_bit01==2'b10);
end

always @(posedge clk )
begin
    if (reset_n == 0)
        espi_kcs1_dataout_cnt <= 8'd0;
    else if(flg_kcs1_status_idle2wr) //idle2write status
        espi_kcs1_dataout_cnt <= 8'd0;
    else if (wr_strobe &&(address==5'h0d))
        espi_kcs1_dataout_cnt <= writedata[7:0];
end

always @(posedge clk )
 readdatavalid <= rd_strobe;

always @(posedge clk )
begin
if (rd_strobe)
    case (address)
    5'h00:   readdata[31:0] <= espi_kcs0_base;
    5'h01:   readdata[31:0] <= espi_kcs0_status;
    5'h02:   readdata[31:0] <= {24'd0,espi_kcs0_din};
    5'h03:   readdata[31:0] <= {24'd0,espi_kcs0_dout};
    5'h04:   readdata[31:0] <= {24'd0,espi_kcs0_datain_cnt};
    5'h05:   readdata[31:0] <= {24'd0,espi_kcs0_dataout_cnt};


    5'h08:   readdata[31:0] <= espi_kcs1_base;
    5'h09:   readdata[31:0] <= espi_kcs1_status;
    5'h0a:   readdata[31:0] <= {24'd0,espi_kcs1_din};
    5'h0b:   readdata[31:0] <= {24'd0,espi_kcs1_dout};
    5'h0c:   readdata[31:0] <= {24'd0,espi_kcs1_datain_cnt};
    5'h0d:   readdata[31:0] <= {24'd0,espi_kcs1_dataout_cnt};
    default: readdata[31:0] <= 32'd0;
    endcase
end

// MSI-X interrupt generation logic
//  monitor ibf, cd and bit01.  
//  generate an msi-x when ibf=1,cd=0,bit01=2'd1 (READ STATE)
//  
assign      kcs0_msix   = (espi_kcs0_status_bit01 == 2'b1) & (espi_kcs0_cd == 1'b0) & (espi_kcs0_ibf == 1'b1);
assign      kcs1_msix   = (espi_kcs1_status_bit01 == 2'b1) & (espi_kcs1_cd == 1'b0) & (espi_kcs1_ibf == 1'b1);

    // Delay kcs_msix by one cycle
    always @ (posedge clk)
      if (reset_n == 1'b0)
      begin
        kcs0_msix_1d <= 1'h0;
        kcs1_msix_1d <= 1'h0;
      end
      else
      begin
        kcs0_msix_1d <= kcs0_msix;
        kcs1_msix_1d <= kcs1_msix;
      end
      
    // Transmit a request to send an MSI-X to BMC
    // KCS0
    always @ (posedge clk)
      if (reset_n == 1'b0)
        espi_kcs0_msix_req <= 1'h0;
      else if (kcs_ier == 1'b0)
        espi_kcs0_msix_req <= 1'h0;
      else if (kcs0_msix & ~kcs0_msix_1d) 
        espi_kcs0_msix_req <= 1'h1;
      else if (espi_kcs0_msix_ack) 
        espi_kcs0_msix_req <= 1'h0;

    // Transmit a request to send an MSI-X to BMC
    // KCS1
    always @ (posedge clk)
      if (reset_n == 1'b0)
        espi_kcs1_msix_req <= 1'h0;
      else if (kcs_ier == 1'b0)
        espi_kcs1_msix_req <= 1'h0;
      else if (kcs1_msix & ~kcs1_msix_1d) 
        espi_kcs1_msix_req <= 1'h1;
      else if (espi_kcs1_msix_ack) 
        espi_kcs1_msix_req <= 1'h0;

endmodule
